/*
* Portuguese eID Card API .NET Wrapper
*
* Written by Fernando Nunes (fernando.nunes@devscope.net)
* Copyright (c) 2009 DevScope Information Systems
*
* This library is free software; you can redistribute it and/or 
* modify it under the terms of the GNU Lesser General Public 
* License as published by the Free Software Foundation; either 
* version 2.1 of the License, or (at your option) any later version. 
* 
* This library is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of 
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
* Lesser General Public License for more details. 
* 
* You should have received a copy of the GNU Lesser General Public 
* License along with this library; if not, write to the Free Software 
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 
*/ 


#include <msclr\lock.h>
#include <windows.h>

#include "stdafx.h"
#include "eidpt.h"

using namespace System;
using namespace DevScope::CartaoDeCidadao;

void ChangeAddressHandler(tAddressChangeState state)
{
	// TODO: Test and validate the use of delegate as function pointer
	EIDPT^ pManagedClass = EIDPT::GetInstance();
	if (pManagedClass->ChangeAddressHandler)
		pManagedClass->ChangeAddressHandler((AddressChangeState)state);
}

void ChangePinHandler(tCapPinChangeState state)
{
	// TODO: Test and validate the use of delegate as function pointer
	EIDPT^ pManagedClass = EIDPT::GetInstance();
	if (pManagedClass->ChangePinHandler)
		pManagedClass->ChangePinHandler((CapPinChangeState)state);
}


EIDPT::EIDPT()
{
	// Private constructor
	PTEID_SetChangeAddressCallback(::ChangeAddressHandler);
	PTEID_CAP_SetCapPinChangeCallback(::ChangePinHandler);
}

EIDPT^ EIDPT::GetInstance()
{
	if (!Instance)
	{
		msclr::lock l(instanceLock);
		if (!Instance)
			Instance = gcnew EIDPT();
	}
	return Instance;
}

/// <summary>
/// Initializes the PTEIDLIB (toolkit)
/// <para>This function must be called before any other one.</para>
/// <para>It tries to connect to the card and in case no card is inserted an error is thrown.</para>
/// </summary>
/// <param name="readerName">The name of the SmartCard Reader to use. Pass null to use the first or single card reader.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::Init(String^ readerName)
{
	char* cStr = Interop::netStrToCStr(readerName);
	long result = PTEID_Init(cStr);
	Interop::releaseCStr(cStr);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Closes the PTEIDLIB (toolkit)
/// <para>Cleans up all data used by the middleware, this function must be called if the card is removed from the reader or at the end of the program.</para>
/// </summary>
/// <param name="mode">The <see cref="ExitMode"/> to use when closing.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::Exit(ExitMode mode)
{
	long result = PTEID_Exit((unsigned long)mode);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Retrieves the type of card that is inserted on the reader.
/// </summary>
/// <returns><see cref="CardType"/> enum that represents the citizen's card type.</returns>
CardType EIDPT::GetCardType()
{
	tCardType cardType = PTEID_GetCardType();
	return (CardType)cardType;
}

/// <summary>
/// Retrieves Identification Data from the card.
/// </summary>
/// <returns><see cref="Id"/> Class holding the citizen's information.</returns>
/// <exception cref="EIDPTException"></exception>
Id^ EIDPT::GetID()
{
	PTEID_ID personId = PTEID_ID();
	long result = PTEID_GetID(&personId);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	Id^ managedId = gcnew Id(personId);
	return managedId;
}

/// <summary>
/// Retrieves the Address from the card.
/// </summary>
/// <returns><see cref="Address"/> Class holding the citizen's information.</returns>
/// <exception cref="EIDPTException"></exception>
Address^ EIDPT::GetAddress()
{
	PTEID_ADDR* personAddress = new PTEID_ADDR();
	long result = PTEID_GetAddr(personAddress);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	Address^ managedAddress = gcnew Address(*personAddress);
	return managedAddress;
}

/// <summary>
/// Retrieves the Picture from the card.
/// </summary>
/// <returns><see cref="Picture"/> Class holding the citizen's picture.</returns>
/// <exception cref="EIDPTException"></exception>
Picture^ EIDPT::GetPicture()
{
	PTEID_PIC personPicture = PTEID_PIC();
	long result = PTEID_GetPic(&personPicture);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	Picture^ managedPicture = gcnew Picture(personPicture);
	return managedPicture;
}

/// <summary>
/// Retrieves the certificates that exist on the card.
/// </summary>
/// <returns>Array of <see cref="Certificate"/> Class holding the card and citizen's certificates.</returns>
/// <exception cref="EIDPTException"></exception>
array<Certificate^>^ EIDPT::GetCertificates()
{
	PTEID_Certifs personCertificates = PTEID_Certifs();
	long result = PTEID_GetCertificates(&personCertificates);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	array<Certificate^>^ managedCertificates = gcnew array<Certificate^>(personCertificates.certificatesLength);
	for(int i = 0; i < managedCertificates->Length; ++i)
	{
		managedCertificates[i] = gcnew Certificate(personCertificates.certificates[i]);
	}
	return managedCertificates;
}

/// <summary>
/// Verify a PIN.
/// </summary>
/// <param name="pinId">Which PIN to verify. Refer to Pin Class for Ids.</param>
/// <param name="pin">The specified PIN value. Pass null for the middleware to prompt for the PIN.</param>
/// <returns>The number of tries left for the specified PIN.</returns>
/// <exception cref="EIDPTException"></exception>
long EIDPT::VerifyPIN(Byte pinId, String^ pin)
{
	long cTriesLeft = 0;
	char* cPin = Interop::netStrToCStr(pin);
	long result = PTEID_VerifyPIN(pinId, cPin, &cTriesLeft);
	Interop::releaseCStr(cPin);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return cTriesLeft;
}

/// <summary>
/// Verify a PIN.
/// <para>If this is the signature PIN, does not displays an alert message.</para>
/// </summary>
/// <param name="pinId">Which PIN to verify. Refer to Pin Class for Ids.</param>
/// <param name="pin">The specified PIN value. If null the middleware will prompt for the PIN.</param>
/// <returns>Returns the number of tries left for the specified PIN.</returns>
/// <exception cref="EIDPTException"></exception>
long EIDPT::VerifyPINNoAlert(Byte pinId, String^ pin)
{
	long cTriesLeft = 0;
	char* cPin = Interop::netStrToCStr(pin);
	long result = PTEID_VerifyPIN_No_Alert(pinId, cPin, &cTriesLeft);
	Interop::releaseCStr(cPin);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return cTriesLeft;
}

/// <summary>
/// Change a PIN.
/// </summary>
/// <param name="pinId">Which PIN to change. Refer to <see cref="PinId"/> or <see cref="PinIdv101"/> for Ids.</param>
/// <param name="oldPin">The old PIN value. If null the middleware will prompt for the PIN.</param>
/// <param name="newPin">The new PIN value. If null the middleware will prompt for the PIN.</param>
/// <returns>Returns the number of tries left for the specified PIN.</returns>
/// <exception cref="EIDPTException"></exception>
long EIDPT::ChangePIN(Byte pinId, String^ oldPin, String^ newPin)
{
	long cTriesLeft = 0;
	char* cPinOld = Interop::netStrToCStr(oldPin);
	char* cPinNew = Interop::netStrToCStr(newPin);
	long result = PTEID_ChangePIN(pinId, cPinOld, cPinNew, &cTriesLeft);
	Interop::releaseCStr(cPinOld);
	Interop::releaseCStr(cPinNew);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return cTriesLeft;
}

/// <summary>
/// Retrieves the PINs that exist on the card.
/// </summary>
/// <returns>Array of <see cref="Pin"/> Class holding the PINs information.</returns>
/// <exception cref="EIDPTException"></exception>
array<Pin^>^ EIDPT::GetPINs()
{
	PTEID_Pins personPins = PTEID_Pins();
	long result = PTEID_GetPINs(&personPins);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	array<Pin^>^ managedPins = gcnew array<Pin^>(personPins.pinsLength);
	for(int i = 0; i < managedPins->Length; ++i)
	{
		managedPins[i] = gcnew Pin(personPins.pins[i]);
	}
	return managedPins;
}

/// <summary>
/// Retrieves the PKCS15 Token contents.
/// </summary>
/// <returns><see cref="TokenInfo"/> Class holding the card's PKCS15 Token.</returns>
/// <exception cref="EIDPTException"></exception>
TokenInfo^ EIDPT::GetTokenInfo()
{
	PTEID_TokenInfo tokenInfo = PTEID_TokenInfo();
	long result = PTEID_GetTokenInfo(&tokenInfo);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	TokenInfo^ managedToken = gcnew TokenInfo(tokenInfo);
	return managedToken;
}

/// <summary>
/// Retrieves the SOD file.
/// </summary>
/// <returns>Array of Bytes that represent the SOD file contents.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::ReadSOD()
{
	unsigned long managedSize = 65535;
	array<Byte>^ managedFile = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cFile = &managedFile[0];
	long result = PTEID_ReadSOD(cFile, &managedSize);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	if (managedFile->Length != managedSize)
		Array::Resize(managedFile, managedSize);
	return managedFile;
} 

/// <summary>
/// Unblock a PIN.
/// </summary>
/// <param name="pinId">Which PIN to unblock. Refer to <see cref="PinId"/> or <see cref="PinIdv101"/> for Ids</param>
/// <param name="puk">The PUK value, if null the middleware will prompt for the PUK.</param>
/// <param name="newPin">The new PIN value, if null the middleware will prompt for the PIN.</param>
/// <returns>The number of tries left for the specified PUK.</returns>
/// <exception cref="EIDPTException"></exception>
long EIDPT::UnblockPIN(Byte pinId, String^ puk, String^ newPin)
{
	long cTriesLeft = 0;
	char* cPuk = Interop::netStrToCStr(puk);
	char* cNewPin = Interop::netStrToCStr(newPin);
	long result = PTEID_UnblockPIN(pinId, cPuk, cNewPin, &cTriesLeft);
	Interop::releaseCStr(cPuk);
	Interop::releaseCStr(cNewPin);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return cTriesLeft;
}

/// <summary>
/// Unblock a PIN.
/// </summary>
/// <param name="pinId">Which PIN to unblock. Refer to <see cref="PinId"/> or <see cref="PinIdv101"/> for Ids</param>
/// <param name="puk">The PUK value, if null the middleware will prompt for the PUK.</param>
/// <param name="newPin">The new PIN value, if null the middleware will prompt for the PIN.</param>
/// <param name="flags">The type of PIN change. Refer to <c>PinUnblockType</c> enum for flags.</param>
/// <returns>The number of tries left for the specified PUK.</returns>
/// <exception cref="EIDPTException"></exception>
long EIDPT::UnblockPIN_Ext(Byte pinId, String^ puk, String^ newPin, unsigned long flags)
{
	long cTriesLeft = 0;
	char* cPuk = Interop::netStrToCStr(puk);
	char* cNewPin = Interop::netStrToCStr(newPin);
	long result = PTEID_UnblockPIN_Ext(pinId, cPuk, cNewPin, &cTriesLeft, flags);
	Interop::releaseCStr(cPuk);
	Interop::releaseCStr(cNewPin);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return cTriesLeft;
}

/// <summary>
/// Select an Application Directory File.
/// </summary>
/// <param name="adf">The Application ID of the ADF.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::SelectADF(array<Byte>^ adf)
{
	pin_ptr<unsigned char> cAdf = &adf[0];
	long result = PTEID_SelectADF((unsigned char*)cAdf, sizeof(cAdf));
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Read a file on the card.
/// <para>If a PIN reference is provided and needed to read the file, the middleware will prompt for the PIN.</para>
/// </summary>
/// <param name="filePath">An Array of Bytes specifying the file path (i.e.: {0x3F, 0x00, 0x5F, 0x00, 0xEF, 0x02} for the ID file).</param>
/// <param name="pinId">The ID of the PIN (i.e.: Address PIN Id when reading the Address File).</param>
/// <returns>Array of Bytes that represent the requested file contents.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::ReadFile(array<Byte>^ filePath, Byte pinId)
{
	pin_ptr<unsigned char> cPath = &filePath[0];
	unsigned long managedSize = 65535;
	array<Byte>^ managedFile = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cFile = &managedFile[0];
	long result = PTEID_ReadFile(cPath, filePath->Length, cFile, &managedSize, pinId);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	if (managedFile->Length != managedSize)
		Array::Resize(managedFile, managedSize);
	return managedFile;
}   

/// <summary>
/// Write data to a file on the card.
/// <para>If a PIN reference is provided and needed to write the file, the middleware will prompt for the PIN.</para>
/// <para>This function is only applicable for writing to the Personal Data file.</para>
/// </summary>
/// <param name="filePath">An Array of Bytes specifying the file path (i.e.: {0x3F, 0x00, 0x5F, 0x00, 0xEF, 0x02} for the ID file).</param>
/// <param name="fileBytes">The file bytes.</param>
/// <param name="pinId">The ID of the Authentication PIN. Refer to <see cref="PinId"/> or <see cref="PinIdv101"/> for Ids.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::WriteFile(array<Byte>^ filePath, array<Byte>^ fileBytes, Byte pinId)
{
	pin_ptr<unsigned char> cPath = &filePath[0];
	pin_ptr<unsigned char> cBytes = &fileBytes[0];
	long result = PTEID_WriteFile(cPath, filePath->Length, cBytes, fileBytes->Length, pinId);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
} 

/// <summary>
/// Retrieves the activation status of the card.
/// </summary>
/// <returns><see cref="CardActivationStatus"/> enum that holds the card status.</returns>
/// <exception cref="EIDPTException"></exception>
CardActivationStatus EIDPT::IsActivated()
{
	unsigned long isActivated = 0;
	long result = PTEID_IsActivated(&isActivated);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	CardActivationStatus activationStatus = (CardActivationStatus)isActivated;
	return activationStatus;
} 

/// <summary>
/// Activates the card.
/// <para>If the card has already been activated an exception with SC_ERROR_NOT_ALLOWED is thrown.</para>
/// </summary>
/// <param name="pin">The value of the Activation PIN.</param>
/// <param name="activationDate">The current date in DD MM YY YY format in BCD format (4 bytes) (i.e. {0x17 0x11 0x20 0x06} for the 17th of Nov. 2006).</param>
/// <param name="mode">The Card Activation Mode PIN. Refer to CardActivationMode enum for flags.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::Activate(String^ pin, array<Byte>^ activationDate, CardActivationMode mode)
{
	char* cPin = Interop::netStrToCStr(pin);
	pin_ptr<unsigned char> cDate = &activationDate[0];
	long result = PTEID_Activate(cPin, cDate, (unsigned long)mode);
	Interop::releaseCStr(cPin);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}       

/// <summary>
/// Enable or disable SOD checking.
/// <para>'SOD' checking means that the validity of the ID data, address data, the photo and the card authentication public key is checked to ensure it is not forged.</para>
/// <para>This is done by reading the SOD file which contains hashes over the above data and is signed by a DocumentSigner certificate.</para>
/// </summary>
/// <param name="doCheck">True to enable or False to disable.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::SetSODChecking(bool doCheck)
{
	long result = PTEID_SetSODChecking((int)doCheck);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Specifies the (root) certificates that are used to sign the DocumentSigner certificates in the SOD file.
/// <para>(The SOD file in the card is signed by a DocumentSigner certificates, and this certificate is inside the SOD as well)</para>
/// <para>By default, this library reads the certificates that are present in the %appdir%/eidstore/certs dir (in which %appdir% is the directory in which the application resides.</para>
/// <para>So if this directory doesn't exist (or doesn't contain the correct certificate for the card), you should call this function to specify them or turn off SOD checking.</para>
/// <para>If you call this function again with NULL as parameter, then the default CA certificates will be used again.</para>
/// </summary>
/// <param name="certificates">The (root) certificates that are used to sign the DocumentSigner certificates in the SOD file</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::SetSODCAs(array<Certificate^>^ certificates)
{
	PTEID_Certifs personCertificates = PTEID_Certifs();
	for(int i = 0; i < certificates->Length; ++i)
		personCertificates.certificates[i] = certificates[i]->ToUnmanaged();
	personCertificates.certificatesLength = certificates->Length;
	long result = PTEID_SetSODCAs(&personCertificates);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}       

/// <summary>
/// Retrieves the card authentication key.
/// <para>Gets the public key 'card authentication' key which can be used to verify the authenticity of the eID card.</para>
/// <para>This public key is encoded in the ID file, and should not be confused with the 'Citizen Authentication key'.</para>
/// </summary>
/// <returns><see cref="RSAPublicKey"/> Class containing the card's Public Key.</returns>
/// <exception cref="EIDPTException"></exception>
RSAPublicKey^ EIDPT::GetCardAuthenticationKey()
{
	unsigned char modulus[128];
	unsigned char exponent[3];
	PTEID_RSAPublicKey publicKey = {modulus, sizeof(modulus), exponent, sizeof(exponent) };
	long result = PTEID_GetCardAuthenticationKey(&publicKey);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	RSAPublicKey^ managedPK = gcnew RSAPublicKey(publicKey);
	return managedPK;
}

/// <summary>
/// Retrieves the CVC Certificate Authority Public Key.
/// <para>Get the CVC CA public key that this card uses to verify the CVC key allowing the application to select the correct CVC certificate for this card.</para>
/// <para>This public key is encoded in the ID file, and should not be confused with the 'Citizen Authentication key'.</para>
/// </summary>
/// <returns><see cref="RSAPublicKey"/> Class containing the CVC CA public key.</returns>
/// <exception cref="EIDPTException"></exception>
RSAPublicKey^ EIDPT::GetCVCRoot()
{
	unsigned char modulus[128];
	unsigned char exponent[3];
	PTEID_RSAPublicKey publicKey = {modulus, sizeof(modulus), exponent, sizeof(exponent) };
	long result = PTEID_GetCVCRoot(&publicKey);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	RSAPublicKey^ managedPK = gcnew RSAPublicKey(publicKey);
	return managedPK;
} 

/// <summary>
/// Starts a CVC authentication with the card.
/// <para>The resulting challenge should be signed with the private key corresponding to the CVC certificate (raw RSA signature) and provided in the CVC_Authenticate() function.</para>
/// </summary>
/// <param name="certificate">The Application CVC as a Byte Array.</param>
/// <returns>Array of Bytes that represent the challenge to be signed with a private key.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::CVC_Init(array<Byte>^ certificate)
{
	pin_ptr<unsigned char> cCert = &certificate[0];
	unsigned long managedSize = 128;
	array<Byte>^ managedChallenge = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cChallenge = &managedChallenge[0];
	// Shouldn't this return/set the challenge size ?
	long result = PTEID_CVC_Init(cCert, certificate->Length, cChallenge, managedSize);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	//if (managedChallenge->Length != managedSize)
	//	Array::Resize(managedChallenge, managedSize);
	return managedChallenge;
} 

/// <summary>
/// Finishes the CVC authentication with the card.
/// <para>To be called after a CVC_Init() function.</para>
/// </summary>
/// <param name="signedChallenge">The challenge that was signed with the private key corresponding to the CVC.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CVC_Authenticate(array<Byte>^ signedChallenge)
{
	pin_ptr<unsigned char> cSignedChallenge = &signedChallenge[0];
	long result = PTEID_CVC_Authenticate(cSignedChallenge, signedChallenge->Length);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
} 

/// <summary>
/// Read a file over a 'CVC channel'.
/// <para>A successfull CVC_Init() and CVC_Authenticate() must have been done before.</para>
/// </summary>
/// <param name="filePath">An Array of Bytes specifying the file path (i.e.: {0x3F, 0x00, 0x5F, 0x00, 0xEF, 0x02} for the ID file).</param>
/// <returns>Array of Bytes that represent the requested file contents.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::CVC_ReadFile(array<Byte>^ filePath)
{
	pin_ptr<unsigned char> cPath = &filePath[0];
	unsigned long managedSize = 65535;
	array<Byte>^ managedFile = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cFile = &managedFile[0];
	long result = PTEID_CVC_ReadFile(cPath, filePath->Length, cFile, &managedSize);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	if (managedFile->Length != managedSize)
		Array::Resize(managedFile, managedSize);
	return managedFile;
} 

/// <summary>
/// Write to a file over a 'CVC channel'.
/// <para>A successfull CVC_Init() and CVC_Authenticate() must have been done before.</para>
/// </summary>
/// <param name="filePath">An Array of Bytes specifying the file path (i.e.: {0x3F, 0x00, 0x5F, 0x00, 0xEF, 0x02} for the ID file).</param>
/// <param name="fileOffset">At which offset in the file to start writing.</param>
/// <param name="fileBytes">The file bytes.</param>
/// <param name="mode">The write mode. Refer to <see cref="CVCWriteMode"/> for flags.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CVC_WriteFile(array<Byte>^ filePath, unsigned long fileOffset, array<Byte>^ fileBytes, unsigned long mode)
{
	pin_ptr<unsigned char> cPath = &filePath[0];
	pin_ptr<unsigned char> cFile = &fileBytes[0];
	long result = PTEID_CVC_WriteFile(cPath, filePath->Length, fileOffset, cFile, fileBytes->Length, mode);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Retrieves the Address over a 'CVC channel'.
/// <para>A successfull CVC_Init() and CVC_Authenticate() must have been done before.</para>
/// </summary>
/// <returns><see cref="Address"/> Class holding the citizen's Address information.</returns>
/// <exception cref="EIDPTException"></exception>
Address^ EIDPT::CVC_GetAddress()
{
	PTEID_ADDR personAddress = PTEID_ADDR();
	long result = PTEID_CVC_GetAddr(&personAddress);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	Address^ managedAddress = gcnew Address(personAddress);
	return managedAddress;
} 

/// <summary>
/// Writes the Address over a 'CVC channel'.
/// <para>A successfull CVC_Init() and CVC_Authenticate() must have been done before.</para>
/// </summary>
/// <param name="addressData"><see cref="Address"/> Class holding the citizen's Address information.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CVC_WriteAddress(Address^ addressData)
{
	long result = PTEID_CVC_WriteAddr(&addressData->ToUnmanaged());
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
} 

/// <summary>
/// Writes the SOD file over a 'CVC channel'.
/// <para>A successfull CVC_Init() and CVC_Authenticate() must have been done before.</para>
/// </summary>
/// <param name="fileOffset">At which offset in the file to start writing.</param>
/// <param name="fileBytes">The file bytes.</param>
/// <param name="mode">The write mode. Refer to CVCWriteMode for flags.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CVC_WriteSOD(unsigned long fileOffset, array<Byte>^ fileBytes, unsigned long mode)
{
	pin_ptr<unsigned char> cIn = &fileBytes[0];
	long result = PTEID_CVC_WriteSOD(fileOffset, (unsigned char *)cIn, fileBytes->Length, mode);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Only for IAS 1.0.1 cards
/// <para>Execute this function first to start a secure session with the card using symmetric keys.</para>
/// </summary>
/// <returns>Array of Bytes that represent the challenge to be signed with a private key.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::CVC_Init_SM101()
{
	unsigned long managedSize = 48;
	array<Byte>^ managedChallenge = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cChallenge = &managedChallenge[0];
	// Shouldn't this return/set the challenge size ?
	long result = PTEID_CVC_Init_SM101(cChallenge, managedSize);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	//if (managedChallenge->Length != managedSize)
	//	Array::Resize(managedChallenge, managedSize);
	return managedChallenge;
}

/// <summary>
/// Only for IAS 1.0.1 cards
/// <para>2nd command to be executed to establish a secure session with the card using symmetric keys.</para>
/// </summary>
/// <param name="challenge">The challenge that was signed by the derived symmetric key.</param>
/// <param name="serialNr">The serial number of the application.</param>
/// <param name="cardSerialNr">The serial number of the card.</param>
/// <param name="keyIfd">The secret key kIFD generated by the terminal.</param>
/// <param name="encKey">The derived encoding key for mutual authentication.</param>
/// <param name="macKey">The derived MAC key for mutual authentication.</param>
/// <returns>Array of Bytes with the response from the card to the authentication.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::CVC_Authenticate_SM101(array<Byte>^ challenge, array<Byte>^ serialNr, array<Byte>^ cardSerialNr, array<Byte>^ keyIfd, array<Byte>^ encKey, array<Byte>^ macKey)
{
	pin_ptr<unsigned char> cChallenge = &challenge[0];
	pin_ptr<unsigned char> cSerialNr = &serialNr[0];
	pin_ptr<unsigned char> cCardSerialNr = &cardSerialNr[0];
	pin_ptr<unsigned char> cKeyIfd = &keyIfd[0];
	pin_ptr<unsigned char> cEncKey = &encKey[0];
	pin_ptr<unsigned char> cMacKey = &macKey[0];

	unsigned long managedSize = 128;
	array<Byte>^ managedResponse = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cChallengeResponse = &managedResponse[0];

	long result = PTEID_CVC_Authenticate_SM101(cChallenge, challenge->Length, (char*)cSerialNr, serialNr->Length, (char*)cCardSerialNr, cardSerialNr->Length, cKeyIfd, keyIfd->Length, cEncKey, encKey->Length, cMacKey, macKey->Length, cChallengeResponse, (int*)&managedSize);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	if (managedResponse->Length != managedSize)
		Array::Resize(managedResponse, managedSize);
	return managedResponse;
}

/*
The functions below can be used to help set up a secure channel between
the card and a Server (e.g. Address Change Server).
More specifically, the following steps need to be done:
- PTEID_CVC_R_Init()
- PTEID_CVC_R_DH_Auth()
- PTEID_CVC_R_ValidateSignature()
- a sequence of SendApdu() calls
(the _R_ stands for 'Remote')
*/

/// <summary>
/// Retrieves the Diffie-Hellman parameters
/// <para> This parameters need to be sent to the Server</para>
/// </summary>
/// <returns><see cref="DHParameter"/> Struct that contains the Diffie-Hellman Key Agreement Parameters.</returns>
/// <exception cref="EIDPTException"></exception>
DHParameter^ EIDPT::CVC_R_Init()
{
	DHParameter^ dhParameters = gcnew DHParameter();
	unsigned long pLen = 128;
	unsigned long gLen = 128;
	unsigned long qLen = 20;
	dhParameters->P = gcnew array<Byte>(pLen);
	pin_ptr<unsigned char> cParamP = &dhParameters->P[0];
	dhParameters->G = gcnew array<Byte>(gLen);
	pin_ptr<unsigned char> cParamG = &dhParameters->G[0];
	dhParameters->Q = gcnew array<Byte>(qLen);
	pin_ptr<unsigned char> cParamQ = &dhParameters->G[0];
	long result = PTEID_CVC_R_Init(cParamG, &gLen, cParamP, &pLen, cParamQ, &qLen);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	if (dhParameters->P->Length != pLen)
		Array::Resize(dhParameters->P, pLen);
	if (dhParameters->G->Length != gLen)
		Array::Resize(dhParameters->G, gLen);
	if (dhParameters->Q->Length != qLen)
		Array::Resize(dhParameters->Q, qLen);
	return dhParameters;
}

/// <summary>
/// Complete the Diffie-Hellman key agreement and ask the card for a challenge.
/// <para>More in detail, the following is done:</para>
/// <para> - Check the CVC cert (pucCert) by means of the CA pubkey on the card</para>
/// <para> - Send Kifd (received from the Server) to the card</para>
/// <para> - Read out Kicc, should be sent to the Server</para>
/// <para> - Send the application CVC certificate to the card for verification</para>
/// <para> - Tell the card to use the public key in the CVC certificate</para>
/// <para> - Ask a challenge to the card, should be sent to the Server</para>
/// </summary>
/// <param name="ucKifd"></param>
/// <param name="certificate"></param>
/// <returns><see cref="DHAuthResponse"/> Struct that contains the Diffie-Hellman Authentication Response.</returns>
/// <exception cref="EIDPTException"></exception>
DHAuthResponse^ EIDPT::CVC_R_DH_Auth(array<Byte>^ ucKifd, array<Byte>^ certificate)
{
	pin_ptr<unsigned char> cKifd = &ucKifd[0];
	pin_ptr<unsigned char> cCert = &certificate[0];
	DHAuthResponse^ dhResponse = gcnew DHAuthResponse();
	unsigned long kiccLen = 128;
	unsigned long challengeLen = 128;
	dhResponse->K = gcnew array<Byte>(kiccLen);
	pin_ptr<unsigned char> cKicc = &dhResponse->K[0];
	dhResponse->Challenge = gcnew array<Byte>(challengeLen);
	pin_ptr<unsigned char> cChallenge = &dhResponse->Challenge[0];
	long result = PTEID_CVC_R_DH_Auth(cKifd, ucKifd->Length, cCert, certificate->Length, cKicc, &kiccLen, cChallenge, &challengeLen); 
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return dhResponse;
}

/// <summary>
/// Validate the Signed Signature with the card
/// <para>Provide the signed challenge (made by the Server) to the card.</para>
/// </summary>
/// <param name="signedChallenge">The signed challenge.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CVC_R_ValidateSignature(array<Byte>^ signedChallenge)
{
	pin_ptr<unsigned char> cSignedChallenge = &signedChallenge[0];
	long result = PTEID_CVC_R_ValidateSignature(cSignedChallenge, signedChallenge->Length);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

//TODO: This wrapper method might need to be changed
/// <summary>
/// Sends an APDU to the card. (See ISO7816-4 for more info)
/// <para> - For a case 1 APDU: ulRequestLen should be 4, ulResponseLen should be at least 2 (for SW1-SW2).</para>
/// <para> - For a case 2 APDU: ulRequestLen should be 5.</para>
/// <para> - For a case 3 APDU: ucRequest[4] + 5 should equal ulRequestLen, ulResponseLen should be at least 2 (for SW1-SW2).</para>
/// <para> - For a case 4 APDU: ucRequest[4] + 5 should equal ucRequestLen + 1, the last by is the 'Le' value.</para>
/// <para>If the call has been successfull, the result should always contain SW1 and SW2 at the end.</para>
/// </summary>
/// <param name="request">APDU Command to be sent to the card.</param>
/// <returns>Array of Bytes that represent the card Response APDU.</returns>
/// <exception cref="EIDPTException"></exception>
array<Byte>^ EIDPT::SendAPDU(array<Byte>^ request)
{
	pin_ptr<unsigned char> cUCRequest = &request[0];
	unsigned long managedSize = 256;
	array<Byte>^ managedResponse = gcnew array<Byte>(managedSize);
	pin_ptr<unsigned char> cResponse = &managedResponse[0];
	long result = PTEID_SendAPDU(cUCRequest, request->Length, cResponse, &managedSize);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	if (managedResponse->Length != managedSize)
		Array::Resize(managedResponse, managedSize);
	return managedResponse;
}

/// <summary>
/// Changes the citizen's address.
/// <para>This function will connect to the Address Change Server and forward commands between the Card and the Address Change Server.</para>
/// </summary>
/// <param name="server">Address Change Server, format: "name:port".</param>
/// <param name="serverCACertificate">CA certificate of the Server (DER encoded).</param>
/// <param name="proxyInfo">ProxyInfo Class holding proxy information. null if no proxy is needed.</param>
/// <param name="secretCode">The Secret code that the citizen received to validate its address change.</param>
/// <param name="processCode">The Process code that the citizen received to validate its address change.</param>
/// <exception cref="EIDPTException"></exception>
void EIDPT::ChangeAddress(String^ server, array<Byte>^ serverCACertificate, ProxyInfo^ proxyInfo, String^ secretCode, String^ processCode)
{
	char* cServer = Interop::netStrToCStr(server);
	pin_ptr<unsigned char> cServerCACert = &serverCACertificate[0];
	tProxyInfo cProxyInfo = proxyInfo->ToUnmanaged();
	char* cSecretCode = Interop::netStrToCStr(secretCode);
	char* cProcess = Interop::netStrToCStr(processCode);
	long result = PTEID_ChangeAddress(cServer, cServerCACert, serverCACertificate->Length, &cProxyInfo, cSecretCode, cProcess);
	Interop::releaseCStr(cSecretCode);
	Interop::releaseCStr(cProcess);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
}

/// <summary>
/// Retrieves the Address Change progress.
/// </summary>
/// <returns><see cref="AddressChangeState"/> enum of a running ChangeAddress() operation.</returns>
/// <exception cref="EIDPTException"></exception>
AddressChangeState EIDPT::GetChangeAddressProgress()
{
	tAddressChangeState state = PTEID_GetChangeAddressProgress();
	return (AddressChangeState)state;
}

/// We are masking function pointer as a public delegate of EIDPT
//void EIDPT::SetChangeAddressCallback()//void(_USERENTRY* callback)(AddressChangeState state))
//{
//	PTEID_SetChangeAddressCallback(::ChangeAddressHandler);
//}

/// <summary>
/// Cancels a running ChangeAddress() operation.
/// </summary>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CancelChangeAddress()
{
	PTEID_CancelChangeAddress();
}

/// <summary>
/// Changes the CAP PIN.
/// <para>This function will connect the CAP PIN Change Server and forward commands between the Card and the CAP PIN Server.</para>
/// </summary>
/// <param name="server">Address Change Server, format: "name:port".</param>
/// <param name="serverCACertificate">CA cert of the Server (DER encoded).</param>
/// <param name="proxyInfo">ProxyInfo Class holding proxy information. null if no proxy is needed.</param>
/// <param name="oldPin">The current CAP PIN.</param>
/// <param name="newPin">The new CAP PIN.</param>
/// <returns>The tries left after an unsuccessful attempt.</returns>
/// <exception cref="EIDPTException"></exception>
long EIDPT::CAP_ChangeCapPin(String^ server, array<Byte>^ serverCACertificate, ProxyInfo^ proxyInfo, String^ oldPin, String^ newPin)
{
	char* cServer = Interop::netStrToCStr(server);
	pin_ptr<unsigned char> cServerCACert = &serverCACertificate[0];
	tProxyInfo cProxyInfo = proxyInfo->ToUnmanaged();
	char* cOldPin = Interop::netStrToCStr(oldPin);
	char* cNewPin = Interop::netStrToCStr(newPin);
	long triesLeft = 0;
	long result = PTEID_CAP_ChangeCapPin(cServer, cServerCACert, serverCACertificate->Length, &cProxyInfo, cOldPin, cNewPin, &triesLeft);
	Interop::releaseCStr(cOldPin);
	Interop::releaseCStr(cNewPin);
	if (result != 0)
	{
		EIDPTException^ except = gcnew EIDPTException(result);
		throw except;
	}
	return triesLeft;
}

/// <summary>
/// Retrieves the CAP PIN Change progress.
/// </summary>
/// <returns><see cref="CapPinChangeState"/> enum of a running CAP_ChangeCapPin() operation.</returns>
/// <exception cref="EIDPTException"></exception>
CapPinChangeState EIDPT::CAP_GetCapPinChangeProgress()
{
	tCapPinChangeState state = PTEID_CAP_GetCapPinChangeProgress();
	return (CapPinChangeState)state;
}

/// We are masking function pointer as a public delegate of EIDPT
//void EIDPT::CAP_SetCapPinChangeCallback()//void(_USERENTRY * callback)(CapPinChangeState state))
//{
//	PTEID_CAP_SetCapPinChangeCallback(::ChangePinHandler);
//}

/// <summary>
/// Cancels a running CAP_ChangeCapPin() operation.
/// </summary>
/// <exception cref="EIDPTException"></exception>
void EIDPT::CAP_CancelCapPinChange()
{
	PTEID_CAP_CancelCapPinChange();
}

/// <summary>
/// Retrieves the latest error code received or sent by ChangeAddress() or CAP_ChangeCapPin().
/// </summary>
/// <returns><see cref="WebErrorCode"/> enum containing the error code</returns>
/// <exception cref="EIDPTException"></exception>
WebErrorCode EIDPT::GetLastWebErrorCode()
{
	tWebErrorCode errorCode = PTEID_GetLastWebErrorCode();
	return (WebErrorCode)errorCode;
}

/// <summary>
/// Retrieves the lastest error message received or sent by ChangeAddress() or CAP_ChangeCapPin().
/// </summary>
/// <returns>String containing the error message</returns>
/// <exception cref="EIDPTException"></exception>
String^ EIDPT::GetLastWebErrorMessage()
{
	return Interop::CStrToNetStr((char*)PTEID_GetLastWebErrorMessage());
}

ResultCode EIDPTException::ErrorCode::get()
{
	return (ResultCode)this->errorCode;
}